#!/usr/bin/python3
# -*- coding:utf-8 -*-
import os
import sys
import argparse
from jinja2 import Environment, FileSystemLoader, select_autoescape
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, Optional

class XMLGenerator:
    """基于 Jinja2 的 XML 生成器 (面向对象封装版)"""
    
    def __init__(
        self,
        template_dir: str = "templates",
        output_dir: str = "generated_xml",
        encoding: str = "UTF-8",
        auto_escape: bool = True,
        default_context: Optional[Dict[str, Any]] = None
    ):
        """
        初始化 XML 生成器
        :param template_dir: 模板目录路径
        :param output_dir: 输出文件目录
        :param encoding: XML 文件编码
        :param auto_escape: 是否启用自动转义（安全防护）
        :param default_context: 默认上下文参数
        """
        self.template_dir = Path(template_dir)
        self.output_dir = Path(output_dir)
        self.encoding = encoding
        self.default_context = default_context or {}
        self.data = {
            "clusterName": "gauss",
            "gaussdbAppPath": os.environ.get("GAUSSHOME"),
            "gaussdbLogPath": os.environ.get("GAUSSLOG"),
            "tmpMppdbPath": os.environ.get("PGHOST"),
            "gaussdbToolPath":"None",
            "datanodepath": os.environ.get("PGDATA"),
            "corePath":os.environ.get("GAUSSHOME")+"/corefile",
            "clusterType":os.environ.get("CLUSTERTYPE"),
            "cmDir": os.environ.get("GAUSS_CM_DIR"),
            "arch": "hybrid",
            "datanode": {
                "dataPortBase": 5432,
            },
            "cms": {
                "cmServerPortBase": 20000,
            }
            
        }
        if self.data['cmDir'] is None:
            self.data['cmDir']=self.data['gaussdbAppPath']
        
        if self.data['clusterType'] is None:
            self.data['clusterType']="single-inst"
        if os.environ.get("GS_PORT") is not None:
            self.data["datanode"]["dataPortBase"]=os.environ.get("GS_PORT")
    
        
       
        # 初始化 Jinja2 环境
        self.env = Environment(
            loader=FileSystemLoader(self.template_dir),
            autoescape=select_autoescape(enabled_extensions=('xml', 'j2')) if auto_escape else False,
            trim_blocks=True,
            lstrip_blocks=True
        )
        
        # 注入默认全局变量
        self.update_default_context({
            "generator": "OOP XML Generator",
            "xml_version": "1.0",
            "namespace": "http://example.com/xmlns",
            "timestamp": self._generate_timestamp
        })
    def parse_args(self):
        parser = argparse.ArgumentParser(description="""
        code check pull request parameters.""")
        parser.add_argument('--primary-host', type=str, required=True, help='primary hosts.')
        parser.add_argument('--standby-host', type=str, required=True, help='standby hosts.')
        parser.add_argument('--primary-hostname', type=str, required=True, help='standby host name.')
        parser.add_argument('--standby-hostname', type=str, required=True, help='standby host names.')
        parser.add_argument('--cms-host', type=str, required=False, help='cms hosts.')
        parser.add_argument('--cms-hostname', type=str, required=False, help='cms host names.')
        return parser.parse_args()
    def reset_hosts(self, cluster):
        hash={}
        for idx, hostname in enumerate(cluster["cms"]["hostname"]):
            hash[hostname]={
                "host": cluster["cms"]["host"][idx],
                "priority": 1,
            }
        for idx, hostname in enumerate(cluster["dn"]["hostname"]):
            if hash.get(hostname) is not None:
                hash[hostname]["priority"]=hash[hostname]["priority"]-1
            else:
                hash[hostname]={
                    "host": cluster["dn"]["host"][idx],
                    "priority": -1,
                }
        cluster["index"]=hash
        cms_hosts,cms_hostnames=[],[]
        dn_hosts,dn_hostnames=[],[]
        cluster_hosts,cluster_hostnames=[],[]
        split=True
        for idx, hostname in enumerate(cluster["cms"]["hostname"]):
            cms_hostnames.append(hostname)
            cms_hosts.append(hash[hostname]["host"])
            cluster_hostnames.append(hostname)
            cluster_hosts.append(hash[hostname]["host"])
            if hash[hostname]["priority"]==0:
                dn_hostnames.append(hostname)
                dn_hosts.append(hash[hostname]["host"])
                split=False
        for idx, hostname in enumerate(cluster["dn"]["hostname"]):
            if hash[hostname]["priority"]==-1:
                dn_hostnames.append(hostname)
                dn_hosts.append(hash[hostname]["host"])
                cluster_hostnames.append(hostname)
                cluster_hosts.append(hash[hostname]["host"])
        cluster["dn"]["hostname"]=dn_hostnames
        cluster["dn"]["host"]=dn_hosts
        cluster["cms"]["hostname"]=cms_hostnames
        cluster["cms"]["host"]=cms_hosts
        cluster["host"]=cluster_hosts
        cluster["hostname"]=cluster_hostnames
        if split:
            cluster["arch"]="split"
        else:
            cluster["arch"]="mixed"


    def update_data(self) -> None:
        params = self.parse_args()
        cluster={
            "dn":{
              "hostname": [params.primary_hostname] + params.standby_hostname.split(","), 
              "host": [ params.primary_host] + params.standby_host.split(","),
              "hostnamestr": params.primary_hostname + ","+params.standby_hostname, 
              "hoststr": params.primary_host + ","+params.standby_host, 
            }
        }
        cluster["cms"] = {
                "hostname": cluster["dn"]["hostname"],
                "host": cluster["dn"]["host"],
                "hostnamestr":cluster["dn"]["hostnamestr"], 
                "hoststr":cluster["dn"]["hoststr"],   
        }
        cluster["arch"]="hybrid"
        cluster["host"]=cluster["dn"]["host"]
        cluster["hostname"]=cluster["dn"]["hostname"]
        if (params.cms_host is not None) and (params.cms_hostname is not None):
            cluster["cms"] = {
                "host":params.cms_host.split(","),
                "hostname":params.cms_hostname.split(","),
                "hoststr": params.cms_host,
                "hostnamestr": params.cms_hostname,
            }
            cluster["arch"]=None
        elif (params.cms_host is None) and (params.cms_hostname is not None):
            sys.exit(1)
        elif (params.cms_host is None) and (params.cms_hostname is not None):
            sys.exit(1)
        else:
            cluster["index"]={}
            for idx, hostname in enumerate(cluster["dn"]["hostname"]):
                cluster["index"][hostname]={
                    "host": cluster["dn"]["host"][idx],
                    "priority": 0, 
                }
        if cluster["arch"] is None:
            self.reset_hosts(cluster)
        # AZ need to refactor
        az={
            "name": "AZ1",
            "priority": 1
        }
        devices=[]
        datanodes=[self.data["datanodepath"]]
        for idx, hostname in enumerate(cluster["hostname"]):
            if cluster["index"][hostname]["priority"]<1 and  hostname!=cluster["dn"]["hostname"][0]:
                datanodes.append(hostname)
                datanodes.append(self.data["datanodepath"])
    
            device={
                "sn": 10000001+idx,
                "host": cluster["host"][idx],
                "hostname": hostname,
                "az": az
            }
            if hostname==cluster["cms"]["hostname"][0]:
                device["role"]="primary"
                device["component"]="cms"
            if hostname==cluster["dn"]["hostname"][0]:
                device["role"]="primary"
                if device.get("component") =="cms":
                    device["component"]="hybrid"
                else:
                    device["component"]="dn"
            devices.append(device)
        self.data['devices']=devices

        self.data["nodeNames"]=",".join(cluster["hostname"])
        self.data["nodeIps"]=",".join(cluster["host"])
        # CMServer default configure items
        # at present, cmServerlevel should be 1
        cms=self.data["cms"]
        cms["level"]=1
        # At present, cmsNum should be 1, 
        # or it will not generate static configure file
        cms["num"]=1
        if cms.get("Relation") is None:
            cms["Relation"]= cluster["cms"]["hostnamestr"]
        # !! Becarefull with ListenIp1 and HaIp1 conifgure item.
        if cms.get("ListenIp1") is None:
            cms["ListenIp1"]=cluster["cms"]["hoststr"]
        if cms.get("HaIp1") is None:
            cms["HaIp1"]=cluster["cms"]["hoststr"]
        
        #print("cmServer",self.data["cms"]) 
        # Datanode default configure items
        dn=self.data["datanode"]
        dn["datanodes"]=",".join(datanodes)
        # At present, dataNum must be 1,
        # or it will not generate static configure file
        dn["dataNum"]=1
        if dn.get("dataPortStandby") is None:
            dn["dataPortStandby"]=dn["dataPortBase"]
        if dn.get("dataPortDummyStandby") is None:
            dn["dataPortDummyStandby"]=dn["dataPortBase"]
        #print("dn",self.data["datanode"])
        print("Output cluter configure with '%s' type." % (cluster["arch"]))
        #sys.exit()
        

    def _generate_timestamp(self) -> str:
        """生成 ISO8601 格式时间戳"""
        return datetime.utcnow().isoformat() + "Z"

    def update_default_context(self, context: Dict[str, Any]) -> None:
        """更新默认上下文参数"""
        self.default_context.update(context)

    def render(
        self,
        template_name: str,
        output_filename: Optional[str] = None
    ) -> str:
        """
        渲染 XML 内容并保存文件
        :param template_name: 模板文件名（需位于 template_dir）
        :param context: 动态上下文数据
        :param output_filename: 输出文件名（默认使用模板名）
        :return: 生成的 XML 内容
        """
        # 合并上下文数据
        merged_context = {**self.default_context, **(self.data or {})}
        
        # 加载模板
        template = self.env.get_template(template_name)
        
        # 渲染内容
        xml_content = template.render(merged_context)
        
        # 自动生成输出路径
        if output_filename is None:
            output_filename = f"{Path(template_name).stem}.xml"
        
        output_path = self.output_dir / output_filename
        
        # 确保输出目录存在
        output_path.parent.mkdir(parents=True, exist_ok=True)
        
        # 写入文件
        with output_path.open("w", encoding=self.encoding) as f:
            f.write(xml_content)
        
        print(f"Generate XML configure sucessfully -> {output_path.resolve()}")
        return xml_content

    @classmethod
    def from_config(cls, config_path: str) -> "XMLGenerator":
        """从配置文件创建实例（示例工厂方法）"""
        # 这里可扩展为实际配置加载逻辑
        return cls()

# 使用示例 ==============================================
if __name__ == "__main__":
    # 初始化生成器
    script_dir=os.path.dirname(os.path.abspath(__file__))
    print(script_dir)
    xml_gen = XMLGenerator(
        template_dir=script_dir+"/templates",
        output_dir=script_dir,
        auto_escape=True
    )
    xml_gen.update_data()
    
    # 添加项目默认参数
    xml_gen.update_default_context({
        "author": "XML Generator Team",
        "precision": 2
    })
    
    # 生成 XML
    xml_content = xml_gen.render(
        template_name="cluster.xml.j2",
        output_filename="cluster.xml"
    )
    
    # 打印部分内容
    print("\n生成内容预览:")
    print(xml_content[:600] + "...")  # 显示前300字符